﻿Public Class Notificator
    ' TODO 1ページがカウント0 or リードできないなら 90秒休止

    Private LiveProgramDictionary As New Dictionary(Of Integer, LiveProgram)
    Private IsStarted As Boolean

    'Private DateTimeLockSlim As New System.Threading.ReaderWriterLockSlim
    'Private DateTimeDictionary As New Dictionary(Of Integer, DateTime)
    'Private WithEvents Timer As New Timers.Timer
    'Private LiveSite As New NicoLiveSite

    Private AlivePrograms As New Dictionary(Of Integer, Integer) ' Value: 生存確認できなかった回数

#Region "NicoLiveFeed"

    Private NicoLiveFeed As New NicoLiveFeed
    Private Enum NicoLiveFeedSchedule
        None
        All
        Started
        Closed
    End Enum
    Private FeedSchedule As New NicoLiveFeedSchedule
    Private MaxLiveProgramCountPerPage As Integer
    Private LatestLastPage As Integer
    Private Const LimitPage As Integer = 100
    Private WithEvents FeedTimer As New Timers.Timer

#End Region

#Region "NicoLiveCommentServer"

    Private WithEvents NicoLiveCommentServer As New NicoLiveCommentServer

#End Region

    ' TimeTable
    ' OrderedOnAirIds(kind)
    ' AntenaIds
    ' OffAirIds(kind)
    ' AlertedStationsIds

#Region "Events"

    Public Event LiveProgramStartedOrUpdated As EventHandler(Of LiveProgramEventArgs)
    Protected Sub OnLiveProgramStartedOrUpdated(ByVal e As LiveProgramEventArgs)
        RaiseEvent LiveProgramStartedOrUpdated(Me, e)
    End Sub

    Public Event LiveProgramUpdated As EventHandler(Of LiveProgramEventArgs)
    Protected Sub OnLiveProgramUpdated(ByVal e As LiveProgramEventArgs)
        RaiseEvent LiveProgramUpdated(Me, e)
    End Sub

    Public Event LiveProgramClosed As EventHandler(Of LiveProgramIdEventArgs)
    Protected Sub OnLiveProgramClosed(ByVal e As LiveProgramIdEventArgs)
        RaiseEvent LiveProgramClosed(Me, e)
    End Sub

    Public Event LiveProgramPreviewStart As EventHandler(Of PreviewLiveProgramEventArgs)
    Protected Sub OnLiveProgramPreviewStart(ByVal e As PreviewLiveProgramEventArgs)
        RaiseEvent LiveProgramPreviewStart(Me, e)
    End Sub

#End Region

#Region "Properties"

    Private _UserAgent As String = ""
    Public Property UserAgent As String
        Get
            Return _UserAgent
        End Get
        Set(ByVal value As String)
            _UserAgent = value
            NicoLiveCommentServer.UserAgent = value
            NicoLiveFeed.UserAgent = value
        End Set
    End Property


#End Region

    Public Sub Start()
        IsStarted = True

        'NicoLiveCommentServer.Connect()

        FeedSchedule = NicoLiveFeedSchedule.All
        FeedTimer.Interval = 100
        FeedTimer.Start()

        'Dim site = New NicoLiveSite
        'site.IsClosed(New LiveProgram With {.Id = 9081902})

        'Timer.Interval = 1000
        'Timer.Start()
    End Sub

    Public Sub [End]()
        IsStarted = False

        NicoLiveCommentServer.Disconnect()
        FeedTimer.Stop()
    End Sub


    Private Sub FeedTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles FeedTimer.Elapsed
        FeedTimer.Stop()

        'For i = 0 To 15
            'Dim p = New LiveProgram
            'p.Id = i
            'p.Title = i.ToString
            'p.Station.Id = "co" & p.Id.ToString
            'p.IsOnAir = (i < 10)
            'OnLiveProgramStartedOrUpdated(New LiveProgramEventArgs(p))
        'Next
        'For i = 5 To 6
            'OnLiveProgramClosed(New LiveProgramIdEventArgs(i))
        'Next
        'Exit Sub


        Select Case FeedSchedule
            Case NicoLiveFeedSchedule.All
                ReadFeeds(1, LimitPage, NicoLiveFeedSchedule.All)
                FeedSchedule = NicoLiveFeedSchedule.Started

            Case NicoLiveFeedSchedule.Started
                ReadFeeds(1, 5, NicoLiveFeedSchedule.Started)
                FeedSchedule = NicoLiveFeedSchedule.Closed

            Case NicoLiveFeedSchedule.Closed
                ReadFeeds(LatestLastPage - 5, LimitPage, NicoLiveFeedSchedule.Closed)
                FeedSchedule = NicoLiveFeedSchedule.Started
        End Select

        FeedTimer.Interval = 30 * 1000
        FeedTimer.Start()

    End Sub

    Public Sub ChangeStatus()
    End Sub

    Public Sub AddOnAirLivePrograms(ByVal ids As IEnumerable(Of LiveProgram))
    End Sub

    Public Sub AddFavoriteStationIds(ByVal ids As IEnumerable(Of String))
    End Sub

    Public Sub Test()
        Dim oldList = New List(Of LiveProgram)
        Dim newList = New List(Of LiveProgram)

        oldList.Add(New LiveProgram With {.Id = 0, .StartDateTime = Now.AddMinutes(8)})
        oldList.Add(New LiveProgram With {.Id = 1, .StartDateTime = Now.AddMinutes(7)})
        oldList.Add(New LiveProgram With {.Id = 2, .StartDateTime = Now.AddMinutes(6)})
        oldList.Add(New LiveProgram With {.Id = 3, .StartDateTime = Now.AddMinutes(5)})
        oldList.Add(New LiveProgram With {.Id = 4, .StartDateTime = Now.AddMinutes(4)})
        oldList.Add(New LiveProgram With {.Id = 5, .StartDateTime = Now.AddMinutes(3)})
        oldList.Add(New LiveProgram With {.Id = 6, .StartDateTime = Now.AddMinutes(2)})
        oldList.Add(New LiveProgram With {.Id = 7, .StartDateTime = Now.AddMinutes(1)})
        oldList.Add(New LiveProgram With {.Id = 8, .StartDateTime = Now.AddMinutes(1)})
        oldList.Add(New LiveProgram With {.Id = 9, .StartDateTime = Now.AddMinutes(1)})
        oldList.Add(New LiveProgram With {.Id = 10, .StartDateTime = Now.AddMinutes(1)})

        For i = 0 To 10
            oldList(i).Station.Id = i.ToString
        Next

        'list2.Add(New Foo With {.Id = 21, .DateTime = Now.AddMinutes(11)})
        'list2.Add(New Foo With {.Id = 20, .DateTime = Now.AddMinutes(10)})
        'newList.Add(oldList(0))
        'newList.Add(oldList(1))
        '2
        '3
        'newList.Add(oldList(4))
        '5
        Dim p1 = New LiveProgram With {.Id = 20, .StartDateTime = Now.AddMinutes(10)}
        p1.Station.Id = "20"
        newList.Add(p1)

        newList.Add(oldList(5))

        Dim p2 = New LiveProgram With {.Id = 21, .StartDateTime = Now.AddMinutes(10)}
        p2.Station.Id = "21"
        newList.Add(p2)

        'newList.Add(oldList(6))
        'newList.Add(oldList(7))
        '8
        'newList.Add(oldList(9))

        InsertLiveProgram(oldList, newList)
        RemoveLiveProgram(oldList, newList, False)

        For Each i In oldList
            Console.WriteLine(i.Id)
        Next

    End Sub

    Private Sub ReadFeeds(ByVal page As Integer, ByVal endPage As Integer, ByVal schedule As NicoLiveFeedSchedule)
        ' TODO Schedule から page 決定

        Dim isFirstRead = True ' 頭出し操作中か
        Dim isLastPage As Boolean
        Dim ids = New HashSet(Of Integer)

        Do While IsStarted OrElse Not isLastPage
            If page > endPage OrElse page > LimitPage Then
                Exit Do
            End If

            Dim newList = NicoLiveFeed.GetLivePrograms(page)

            If MaxLiveProgramCountPerPage < newList.Count Then
                MaxLiveProgramCountPerPage = newList.Count
            End If

            If schedule = NicoLiveFeedSchedule.Closed Then
                For Each item In newList
                    If ids.Contains(item.Id) Then
                        Continue For
                    End If
                    ids.Add(item.Id)
                Next
            End If


            If newList.Count = 0 AndAlso page = 1 Then
                ' TODO 休止

                'isLastPage = True
                LatestLastPage = page
                Exit Do

            ElseIf newList.Count = 0 AndAlso Not isFirstRead Then

                'isLastPage = True
                LatestLastPage = page - 1

                'TODO 一つ前のRead結果を使って終了処理可能
                Exit Do

            ElseIf newList.Count = 0 AndAlso isFirstRead Then
                endPage = page - 1
                page -= 5
                If page < 0 Then
                    page = 1
                End If
                Continue Do

            ElseIf newList.Count < MaxLiveProgramCountPerPage Then
                isLastPage = True
                LatestLastPage = page

            End If

            If isFirstRead Then
                isFirstRead = False
            End If

            ''Insert proc 開始・更新 判定
            'InsertLiveProgram(LivePrograms, newList)

            ''Delete proc 終了判定
            'RemoveLiveProgram(LivePrograms, newList, isLastPage)

            InsertLivePrograms(newList)

            page += 1
        Loop

        If schedule = NicoLiveFeedSchedule.Closed Then
            Dim removedIds = New List(Of Integer)

            Dim aliveIds = New List(Of Integer)
            For Each id In AlivePrograms.Keys
                aliveIds.Add(id)
            Next

            For Each id In aliveIds
                If ids.Contains(id) Then
                    AlivePrograms(id) = 0
                    Continue For
                End If

                If Not LiveProgramDictionary.ContainsKey(id) Then
                    Continue For
                End If

                AlivePrograms(id) += 1

                ' Remove
                If AlivePrograms(id) >= 3 Then
                    OnLiveProgramClosed(New LiveProgramIdEventArgs(id))

                    removedIds.Add(id)
                End If
            Next

            For Each id In removedIds
                LiveProgramDictionary.Remove(id)
                AlivePrograms.Remove(id)
            Next

            For Each id In ids
                If AlivePrograms.ContainsKey(id) Then
                    Continue For
                End If
                AlivePrograms.Add(id, 0)
            Next
        End If

    End Sub

    Private Sub InsertLivePrograms(ByVal programs As IList(Of LiveProgram))
        Dim removedPrograms = New List(Of LiveProgram)

        For Each targetProgram In programs
            If LiveProgramDictionary.ContainsKey(targetProgram.Id) Then
                ' Update
                LiveProgramDictionary(targetProgram.Id) = targetProgram
                OnLiveProgramUpdated(New LiveProgramEventArgs(targetProgram))
                Continue For
            End If

            ' New live program
            ' 同じコミュで放送が開始しているか確認 → Closed 通知
            Dim sid = targetProgram.Station.Id
            Dim pid = targetProgram.Id

            Dim ps = From p In LiveProgramDictionary.Values
            Where p.Station.Id = sid _
            AndAlso p.Id <> pid _
            AndAlso p.Station.Id <> ""

            If ps.Count > 0 Then
                OnLiveProgramClosed(New LiveProgramIdEventArgs(ps.SingleOrDefault.Id))
                removedPrograms.Add(ps.SingleOrDefault)
            End If

            ' 開始通知
            OnLiveProgramStartedOrUpdated(New LiveProgramEventArgs(targetProgram))
            LiveProgramDictionary.Add(targetProgram.Id, targetProgram)

            ''
            'DateTimeLockSlim.EnterWriteLock()
            'Dim time = targetProgram.StartDateTime.AddSeconds(30 * 60 + 10)
            'If DateTimeDictionary.ContainsKey(targetProgram.Id) Then
            'DateTimeDictionary(targetProgram.Id) = time
            'Else
            'DateTimeDictionary.Add(targetProgram.Id, time)
            'End If
            'DateTimeLockSlim.ExitWriteLock()

        Next

        For i = removedPrograms.Count - 1 To 0 Step -1
            LiveProgramDictionary.Remove(removedPrograms(i).Id)

            'DateTimeLockSlim.EnterWriteLock()
            'If DateTimeDictionary.ContainsKey(removedPrograms(i).Id) Then
            'DateTimeDictionary.Remove(removedPrograms(i).Id)
            'End If
            'DateTimeLockSlim.ExitWriteLock()
        Next
    End Sub


    Private Sub InsertLiveProgram(ByVal oldList As IList(Of LiveProgram), ByVal newList As IList(Of LiveProgram))
        Dim insertIndex = 0
        Dim removedPrograms As New List(Of LiveProgram)

        For j = 0 To newList.Count - 1
            If newList(j).Station.Kind <> StationKind.Community Then
                Continue For
            End If

            Dim targetProgram = newList(j)

            Dim index = -1
            For i = insertIndex To oldList.Count - 1
                If oldList(i).Id = targetProgram.Id Then
                    index = i
                    Exit For
                End If
            Next

            If index >= 0 Then
                insertIndex = index + 1
                ' Update
                OnLiveProgramUpdated(New LiveProgramEventArgs(targetProgram))
                Continue For
            End If

            ' New item
            If insertIndex = oldList.Count - 1 Then
                oldList.Add(targetProgram)
            Else
                oldList.Insert(insertIndex, targetProgram)
            End If
            insertIndex += 1

            ' 同じコミュで放送が開始しているか確認 → Closed 通知
            Dim programs = From p In oldList
                           Where p.Station.Id = targetProgram.Station.Id _
                           AndAlso p.Id <> targetProgram.Id _
                           AndAlso p.Station.Id <> ""

            If programs.Count > 0 Then
                OnLiveProgramClosed(New LiveProgramIdEventArgs(programs.SingleOrDefault.Id))
                removedPrograms.Add(programs.SingleOrDefault)
                'oldList.Remove(programs.SingleOrDefault)
            End If

            ' 開始通知
            OnLiveProgramStartedOrUpdated(New LiveProgramEventArgs(targetProgram))

        Next

        For i = removedPrograms.Count - 1 To 0 Step -1
            oldList.Remove(removedPrograms(i))
        Next

    End Sub

    Private Sub RemoveLiveProgram(ByVal oldList As IList(Of LiveProgram), ByVal newList As IList(Of LiveProgram), ByVal isLastPage As Boolean)

        Dim removedItems = New List(Of LiveProgram)
        'Dim start = 0
        'For i = 0 To newList.Count - 2

        'Dim item1 = newList(i)
        'Dim item2 = newList(i + 1)

        'Dim existItem1 = False
        'For j = start To oldList.Count - 1
        'If oldList(j).Id <> item1.Id Then
        'Continue For
        'End If

        'Dim existItem2 = False
        'Dim items = New List(Of LiveProgram)
        'For k = j + 1 To oldList.Count - 1
        'If oldList(k).Id = item2.Id Then
        'existItem2 = True
        'Exit For
        'End If
        'items.Add(oldList(k))
        'Next

        'If existItem2 AndAlso items.Count > 0 Then
        'removedItems.AddRange(items)
        'Else
        '' new item!
        'End If

        'existItem1 = True
        ''start = j + 1
        'Next

        'If existItem1 = False Then
        'start = 0
        'End If

        'Next

        'For i = removedItems.Count - 1 To 0 Step -1
        'Debug.Print("Removed " & removedItems(i).Id)
        'OnLiveProgramClosed(New LiveProgramIdEventArgs(removedItems(i).Id))
        'oldList.Remove(removedItems(i))
        'Next

        If Not isLastPage Then
            Exit Sub
        End If

        Dim lastItemId = newList(newList.Count - 1).Id
        Dim lastIndex = -1
        For i = oldList.Count - 1 To 0 Step -1
            If oldList(i).Id = lastItemId Then
                lastIndex = i
                Exit For
            End If
        Next

        If lastIndex < 0 Then
            Exit Sub
        End If

        For i = oldList.Count - 1 To lastIndex + 1 Step -1
            OnLiveProgramClosed(New LiveProgramIdEventArgs(oldList(i).Id))
            oldList.RemoveAt(i)
        Next

    End Sub

#Region "NicoLiveCommentServer"

    Private Sub NicoLiveCommentServer_LiveProgramPreviewStart(ByVal sender As Object, ByVal e As PreviewLiveProgramEventArgs) Handles NicoLiveCommentServer.LiveProgramPreviewStart
        OnLiveProgramPreviewStart(e)
    End Sub

    Private Sub NicoLiveCommentServer_LiveProgramStarted(ByVal sender As Object, ByVal e As LiveProgramEventArgs) Handles NicoLiveCommentServer.LiveProgramStarted
        OnLiveProgramStartedOrUpdated(e)
    End Sub

#End Region

    'Private Sub Timer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles Timer.Elapsed

    'Dim dic = New Dictionary(Of Integer, DateTime)

    'DateTimeLockSlim.EnterReadLock()
    'Dim ts = From t In DateTimeDictionary
    'Where t.Value <= Now
    'For Each t In ts
    'dic.Add(t.Key, t.Value)
    'Next
    'DateTimeLockSlim.ExitReadLock()

    'For Each keyValue In dic

    'LiveSite.IsClosed(keyValue.Key)

    'Next

    'End Sub


End Class